home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Info / For Developers / GhostScript 5.10 / MacGS-510 / files / pdf_main.ps < prev    next >
Text File  |  1997-07-19  |  15KB  |  508 lines

  1. %    Copyright (C) 1994, 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % pdf_main.ps
  16. % PDF file- and page-level operations.
  17.  
  18. % We handle the following PDF 1.2 constructs:
  19. %    page number rather than page object in Dest array
  20. % We explicitly ignore the following PDF 1.2 constructs:
  21. %    "Marked content" operators
  22.  
  23. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  24. .currentglobal true .setglobal
  25. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  26. pdfdict begin
  27.  
  28. % For simplicity, we use a single interpretation dictionary for all
  29. % PDF graphics execution, even though this is too liberal.
  30. /pdfopdict mark
  31.   objopdict { } forall
  32.   drawopdict { } forall
  33.   /endstream { exit } bind
  34.   (%%EOF) cvn { exit } bind        % for filters
  35.     % PDF 1.1 operators
  36.   /BX { /BXlevel BXlevel 1 add store } bind
  37.   /EX { /BXlevel BXlevel 1 sub store } bind
  38.     % PDF 1.2 operators
  39.   /BMC { pop } bind
  40.   /BDC { pop pop } bind
  41.   /EMC { }
  42.   /MP { pop } bind
  43.   /DP { pop pop } bind
  44. .dicttomark readonly def
  45.  
  46. % ======================== Main program ======================== %
  47.  
  48. end            % pdfdict
  49. userdict begin
  50.  
  51. /defaultfontname /Times-Roman def
  52.  
  53. % Make sure the registered encodings are loaded, so we don't run the risk
  54. % that some of the indices for their names will overflow the packed
  55. % representation.  (Yes, this is a hack.)
  56. SymbolEncoding pop
  57. DingbatsEncoding pop
  58.  
  59. % Redefine 'run' so it recognizes PDF files.
  60. systemdict begin
  61. /.runps /run load def
  62. /runpdfstring 50 string def        % length is arbitrary
  63. /run
  64.  { dup type /filetype ne { (r) file } if
  65.    dup read
  66.     { dup (%) 0 get eq
  67.        { pop dup //runpdfstring
  68.         % Some invalid files might have extra-long first lines....
  69.       {  { readline } .internalstopped not { pop pop exit } if
  70.         pop =string
  71.       }
  72.      loop
  73.      //runpdfstring (PDF-) anchorsearch
  74.       { pop pop runpdf }
  75.       { pop cvx .runexec }
  76.      ifelse
  77.        }
  78.        { 2 copy unread pop .runps
  79.        }
  80.       ifelse
  81.     }
  82.     { closefile
  83.     }
  84.    ifelse
  85.  } bind odef
  86. /runpdf            % <file> runpdf -
  87.  { userdict begin
  88.    /PSFile where { pop PSFile (w) file /PSout exch def } if
  89.    /Page# null def
  90.    /Page null def
  91.    /DSCPageCount 0 def
  92.    /PDFSave null def
  93.    GS_PDF_ProcSet begin
  94.    pdfdict begin
  95.    pdfopen begin
  96.    Trailer /Root oget /Pages oget /CropBox knownoget
  97.     { mark /CropBox 3 -1 roll /PAGES pdfmark
  98.     }
  99.    if
  100.    /FirstPage where { pop FirstPage } { 1 } ifelse
  101.    1
  102.    /LastPage where { pop LastPage } { pdfpagecount } ifelse
  103.    QUIET not
  104.     { (Processing pages ) print 2 index =only ( through ) print dup =only
  105.       (.\n) print flush
  106.     }
  107.    if
  108.     { dup /Page# exch store
  109.       QUIET not { (Page ) print dup == flush } if
  110.       pdfgetpage pdfshowpage
  111.     } for
  112.    currentdict pdfclose
  113.    end            % temporary dict
  114.    end            % pdfdict
  115.    end            % userdict
  116.  } bind def
  117. end            % systemdict
  118. % Redefine the procedure that the C code uses for running piped input.
  119. % It is OK to use { (%stdin) run } here, because a startjob cannot occur.
  120. /.runstdin {
  121.   { (%stdin) run } execute0
  122. } bind def
  123.  
  124. end            % userdict
  125. pdfdict begin
  126.  
  127. % ======================== File parsing ======================== %
  128.  
  129. % Read the cross-reference and trailer sections.
  130.  
  131. /traileropdict mark
  132.   (<<) cvn { mark } bind
  133.   (>>) cvn /.dicttomark load
  134.   ([) cvn { mark } bind        % ditto
  135.   (]) cvn dup load
  136.   /true true
  137.   /false false
  138.   /null null
  139.   /R { /resolveR cvx 3 packedarray cvx } bind    % see Objects below
  140.   /startxref /exit load
  141. .dicttomark readonly def
  142.  
  143. % Because of EOL conversion, lines with fixed contents might be followed
  144. % by one or more blanks.
  145. /lineeq            % <filestr> <conststr> lineeq <bool>
  146.  { anchorsearch
  147.     { pop { ( ) anchorsearch not { () eq exit } if pop } loop }
  148.     { pop false }
  149.    ifelse
  150.  } bind def
  151. /linene { lineeq not } bind def
  152.  
  153. % Read (mostly scan) the cross-reference table.
  154. /readxref        % <pos> readxref <trailerdict>
  155.  { PDFoffset add PDFfile exch setfileposition
  156.         % In some PDF files, this position actually points to
  157.         % white space before the xref line.  Skip over this here.
  158.    { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
  159.    } loop
  160.    PDFfile exch setfileposition
  161.    PDFfile pdfstring readline pop
  162.    (xref) linene { /readxref cvx /syntaxerror signalerror } if
  163.         % Store the xref table entry position for each object.
  164.         % We only need to read the run headers, not every entry.
  165.     { PDFfile token pop        % first object # or trailer
  166.       dup /trailer eq { pop exit } if
  167.       PDFfile pdfstring readline pop
  168.       token pop            % entry count
  169.       exch pop exch
  170.         % This section might be adding new objects:
  171.         % ensure that Objects and Generations are big enough.
  172.         % Stack: count obj#
  173.       2 copy add
  174.       dup Objects llength gt
  175.        { dup Objects exch lgrowto /Objects exch def }
  176.       if
  177.       dup Generations llength gt
  178.        { dup Generations exch lgrowto /Generations exch def }
  179.       if
  180.       pop
  181.       PDFfile fileposition 3 -1 roll
  182.        { Objects 2 index lget null eq    % later update might have set it
  183.       { Objects 2 index 2 index cvx lput }
  184.          if exch 1 add exch 20 add
  185.        }
  186.       repeat PDFfile exch setfileposition pop
  187.     } loop
  188.    PDFfile traileropdict .pdfrun
  189.  } bind def
  190.  
  191. % Open a PDF file and read the trailer and cross-reference.
  192. /pdfopen        % <file> pdfopen <dict>
  193.  { pdfdict readonly pop        % can't do it any earlier than this
  194.    /PSout where { pop /pdf2psdict where { pop pdf2psdict begin } if } if
  195.    10 dict begin
  196.    /PSLevel1 where { pop } { /PSLevel1 false def } ifelse
  197.    cvlit /PDFfile exch def
  198.    /PDFsource PDFfile def
  199.    PDFfile dup 0 setfileposition pdfstring readstring 
  200.    not {/pdfopen cvx /syntaxerror signalerror} if
  201.    (%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
  202.    length /PDFoffset exch def pop pop
  203.    PDFfile dup dup 0 setfileposition bytesavailable 
  204.     % Scan backwards over trailing control-character garbage
  205.     % (nulls, ^Zs, EOLs).
  206.     { 1 sub 2 copy setfileposition 1 index read pop
  207.       32 ge {exit} if
  208.     } loop 1 sub setfileposition
  209.    prevline (%%EOF) linene { /pdfopen cvx /syntaxerror signalerror } if
  210.    PDFfile exch setfileposition
  211.    prevline cvi        % xref start position
  212.    exch PDFfile exch setfileposition
  213.    prevline (startxref) linene { /pdfopen cvx /syntaxerror signalerror } if
  214.    pop
  215.         % Stack: xrefpos
  216.    /Objects larray def
  217.    /Generations lstring def
  218.     % Read the last cross-reference table.
  219.    readxref /Trailer exch def
  220.    Trailer /Encrypt known
  221.     { pdf_process_Encrypt    % signal error
  222.     }
  223.    if
  224.     % Read any previous cross-reference tables.
  225.    Trailer { /Prev .knownget not { exit } if readxref } loop
  226.     % Create and initialize some caches.
  227.    /PageCount pdfpagecount def
  228.    /PageNumbers PageCount dict def
  229.    /PageIndex PageCount array def
  230.     % Write the DSC header if appropriate.
  231.    [ (%!PS-Adobe-1.0) #dsc
  232.    [ (%%Pages: (atend)) #dsc
  233.    [ (%%EndComments) #dsc
  234.    [ (%%BeginProlog) #dsc
  235.    [ (% This copyright applies to everything between here and the %%EndProlog:) #dsc
  236.    [ (% ) copyright #dsc
  237.    (gs_pdf.ps) #dscfile
  238.    PSLevel1 { (gs_l2img.ps) #dscfile } if
  239.    [ (%%EndProlog) #dsc
  240.     % Copy bookmarks (outline) to the output.
  241.    #?
  242.     { Trailer /Root oget /Outlines knownoget
  243.        { /First knownoget
  244.       { { dup writeoutline /Next knownoget not { exit } if } loop }
  245.      if
  246.        }
  247.       if
  248.     }
  249.    if   
  250.    currentdict end
  251.  } bind def
  252.  
  253. % Write the outline structure for a file.  Uses linkdest (below).
  254. /writeoutline        % <outlinedict> writeoutline -
  255.  { mark
  256.    0 2 index /First knownoget
  257.     { { exch 1 add exch /Next knownoget not { exit } if } loop }
  258.    if
  259.         % stack: dict mark count
  260.    dup 0 eq
  261.     { pop 1 index
  262.     }
  263.     { 2 index /Count knownoget { 0 lt { neg } if } if
  264.       /Count exch 3 index
  265.     }
  266.    ifelse linkdest /Title oget /Title exch /OUT pdfmark
  267.    /First knownoget
  268.     { { dup writeoutline /Next knownoget not { exit } if } loop }
  269.    if
  270.  } bind def
  271.  
  272. % Close a PDF file.
  273. /pdfclose        % <dict> pdfclose -
  274.  { begin
  275.    /PSout where
  276.     { pop
  277.       [ (%%Trailer) #dsc
  278.       [ (%%Pages: ) DSCPageCount #dsc
  279.       PSout closefile
  280.     }
  281.    if
  282.    PDFfile closefile
  283.    end
  284.    pdf2psdict where { pop currentdict pdf2psdict eq { end } if } if
  285.  } bind def
  286.  
  287. % ======================== Page accessing ======================== %
  288.  
  289. % Get a (possibly inherited) attribute of a page.
  290. /pget            % <pagedict> <key> pget <value> -true-
  291.             % <pagedict> <key> pget -false-
  292.  { 2 copy knownoget
  293.     { exch pop exch pop true
  294.     }
  295.     { exch /Parent knownoget
  296.        { exch pget }
  297.        { pop false }
  298.       ifelse
  299.     }
  300.    ifelse
  301.  } bind def
  302.  
  303. % Get the value of a resource on a given page.
  304. /rget            % <resname> <pagedict> <restype> rget <value> -true-
  305.             % <resname> <pagedict> <restype> rget -false-
  306. { exch /Resources pget
  307.    { exch knownoget
  308.       { exch knownoget }
  309.       { pop false }
  310.      ifelse
  311.    }
  312.    { pop pop false
  313.    }
  314.   ifelse
  315. } bind def
  316.  
  317. % Get the total number of pages in the document.
  318. /pdfpagecount        % - pdfpagecount <int>
  319.  { Trailer /Root oget /Pages oget /Count oget
  320.  } bind def
  321.  
  322. % Find the N'th page of the document by iterating through the Pages tree.
  323. % The first page is numbered 1.
  324. /pdffindpage        % <int> pdffindpage <pagedict>
  325.  { dup Trailer /Root oget /Pages oget
  326.     {        % We should be able to tell when we reach a leaf
  327.         % by finding a Type unequal to /Pages.  Unfortunately,
  328.         % some files distributed by Adobe lack the Type key
  329.         % in some of the Pages nodes!  Instead, we check for Kids.
  330.       dup /Kids knownoget not { exit } if
  331.       exch pop null
  332.       0 1 3 index length 1 sub
  333.        { 2 index exch oget
  334.      dup /Kids known { dup /Count oget } { 1 } ifelse
  335.         % Stack: index kids null node count
  336.      dup 5 index ge { pop exch pop exit } if
  337.      5 -1 roll exch sub 4 1 roll pop
  338.        }
  339.       for exch pop
  340.       dup null eq { pop pop 1 null exit } if
  341.     }
  342.    loop
  343.         % Stack: index countleft node
  344.    1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
  345.    exch pop
  346.    PageIndex 2 index 1 sub 2 index put
  347.    PageNumbers 1 index 3 index put
  348.    exch pop
  349.  } bind def
  350.  
  351. % Find the N'th page of the document.
  352. % The first page is numbered 1.
  353. /pdfgetpage        % <int> pdfgetpage <pagedict>
  354.  { PageIndex 1 index 1 sub get dup null ne
  355.     { exch pop }
  356.     { pop pdffindpage }
  357.    ifelse
  358.  } bind def
  359.  
  360. % Find the page number of a page object (inverse of pdfgetpage).
  361. /pdfpagenumber        % <pagedict> pdfpagenumber <int>
  362.  {    % We use the simplest and stupidest of all possible algorithms....
  363.    PageNumbers 1 index .knownget
  364.     { exch pop
  365.     }
  366.     { 1 1 PageCount 1 add    % will give a rangecheck if not found
  367.        { dup pdfgetpage oforce 2 index eq { exit } if pop
  368.        }
  369.       for exch pop
  370.     }
  371.    ifelse
  372.  } bind def
  373.  
  374. % Display a given page.
  375. /boxrect        % [<llx> <lly> <urx> <ury>] boxrect <x> <y> <w> <h>
  376.  { aload pop exch 3 index sub exch 2 index sub
  377.  } bind def
  378. /linkdest        % <link|outline> linkdest
  379.             %   ([/Page <n>] /View <view> | ) <link|outline>
  380.  { dup /Dest knownoget
  381.     {        % Check for a name, to be looked up in Dests.
  382.       dup type /nametype eq
  383.        { Trailer /Root oget /Dests oget exch knownoget
  384.       { dup type /dicttype eq { /D get } if }
  385.       { null }
  386.      ifelse
  387.        }
  388.       if
  389.       dup null eq
  390.        { pop }
  391.        { dup 0 oget
  392.      dup null eq
  393.       { pop }
  394.       { dup type /integertype ne { pdfpagenumber } if
  395.         /Page exch 4 -2 roll
  396.       }
  397.      ifelse
  398.      dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
  399.        }
  400.       ifelse
  401.     }
  402.    if
  403.  } bind def
  404. /annottypes 5 dict dup begin
  405.   /Text
  406.     { mark exch
  407.        { /Rect /Open /Contents }
  408.        { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
  409.       forall pop /ANN pdfmark
  410.     } bind def
  411.   /Link
  412.     { mark exch
  413.        { /Rect /Border }
  414.        { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
  415.       forall linkdest pop /LNK pdfmark
  416.     } bind def
  417. end def
  418.  
  419. /pdfshowpage        % <pagedict> pdfshowpage -
  420.  { dup /Page exch store
  421.    pdfshowpage_init 
  422.    pdfshowpage_setpage 
  423.    save /PDFSave exch store
  424.    (before exec) VMDEBUG
  425.      pdfshowpage_finish
  426.    (after exec) VMDEBUG
  427.    PDFSave restore
  428.  } bind def
  429.  
  430. /pdfpagecontents    % <pagedict> pdfpagecontents <contents>
  431.  { } bind def
  432.  
  433. /pdfshowpage_init     % <pagedict> pdfshowpage_init <pagedict>
  434.  { gsave
  435.    [ (%%Page: ) Page# ( ) 
  436.      DSCPageCount 1 add /DSCPageCount 1 index store #dsc
  437.    [ (GS_PDF_ProcSet begin) #dsc
  438.  } bind def
  439.  
  440. /pdfshowpage_setpage    % <pagedict> pdfshowpage_setpage <pagedict>
  441.  { 
  442.    3 dict    % for setpagedevice
  443.     % Stack: pagedict setpagedict
  444.     % We want to look at Rotate for displays, but not for printers.
  445.     % The following is a hack, but we don't know a better way to do this.
  446.    currentpagedevice /OutputFile known
  447.    /PSout where dup { exch pop } if or not
  448.     { dup /Orientation 3 index /Rotate pget not { 0 } if 90 idiv
  449.     % Rotate specifies *clockwise* rotation!
  450.       neg 3 and put
  451.     }
  452.    if
  453.     % Stack: pagedict setpagedict
  454.    1 index /MediaBox pget
  455.     {            % Set the page size.
  456.       boxrect [ 2 index 5 index sub 2 index 5 index sub ]
  457.     % Stack: pagedict setpagedict llx lly urx ury pagesize
  458.       5 index exch /PageSize exch put
  459.     % Stack: pagedict contents setpagedict llx lly urx ury
  460.       pop pop
  461.       neg exch neg exch
  462.       [ 3 1 roll ]
  463.     % Stack: pagedict setpagedict pageoffset
  464.       1 index exch /PageOffset exch put
  465.     }
  466.    if
  467.     % Stack: pagedict setpagedict
  468.    /setpagedevice 1 #
  469.  } bind def
  470.  
  471. /pdfshowpage_finish    % <pagedict> pdfshowpage_finish -
  472.  {
  473.     % Copy crop box.
  474.    dup /CropBox pget
  475.     { boxrect rectclip
  476.       dup /CropBox knownoget { mark /CropBox 3 -1 roll /PAGE pdfmark } if
  477.     }
  478.    if
  479.  
  480.     % Copy annotations and links.
  481.    dup /Annots knownoget
  482.     { 0 1 2 index length 1 sub
  483.        { 1 index exch oget
  484.          dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
  485.        }
  486.       for pop
  487.     }
  488.    if
  489.  
  490.     % Display the actual page contents.
  491.    2 dict begin
  492.    /BXlevel 0 def
  493.    matrix currentmatrix /beginpage 0 # setmatrix
  494.    /Contents knownoget not { 0 array } if
  495.    dup type /arraytype ne { 1 array astore } if
  496.     { oforce false resolvestream pdfopdict .pdfrun } forall
  497.    /endpage 0 #
  498.    end            % scratch dict
  499.    grestore
  500.    [ (end) #dsc
  501.  } bind def
  502.  
  503. end            % pdfdict
  504. .setglobal
  505.